/*
 我们学习面向对象
   @1 方便我们更好的学习JS
     JS本身就是基于面向对象构建的编程语言
     我们研究和使用JS也应该按照面向对象的思想去操作！ 
   @2 一般只有“插件/组件/框架”封装设计的时候，才会用到面向对象，平时的业务开发很少会碰到！

 掌握“new”
 掌握“prototype”、“__proto__”
 了解“函数的多重角色”
 */

/* 
function Fn() {
  this.x = 100;
  this.y = 200;
  this.getX = function () {
    console.log(this.x);
  }
}
Fn.prototype.getX = function () {
  console.log(this.x);
};
Fn.prototype.getY = function () {
  console.log(this.y);
};
let f1 = new Fn;
let f2 = new Fn;
console.log(f1.getX === f2.getX); //false
console.log(f1.getY === f2.getY); //true
console.log(f1.__proto__.getY === Fn.prototype.getY);//true
console.log(f1.__proto__.getX === f2.getX);//false
console.log(f1.getX === Fn.prototype.getX);//false
console.log(f1.constructor);//Fn
console.log(Fn.prototype.__proto__.constructor);//Object
// 首先看一下执行的是哪一个方法，再确定方法中的THIS，最后算结果
f1.getX();
// 执行的是私有的getX
// this->f1
// console.log(this.x); => f1.x => 100

f1.__proto__.getX();
// 执行的是公有的getX
// this->f1.__proto__「Fn.prototype」
// console.log(this.x); => f1.__proto__.x => undefined

f2.getY();
// 执行的是公有的getY
// this->f2
// console.log(this.y); => f2.y => 200

Fn.prototype.getY();
// 执行的是公有的getY
// this->Fn.prototype
// console.log(this.y); => Fn.prototype.y => undefined 
*/


function Dog(name) {
  this.name = name;
}
Dog.prototype.bark = function () {
  console.log('wangwang');
}
Dog.prototype.sayName = function () {
  console.log('my name is ' + this.name);
}
/* let sanmao = new Dog('三毛');
console.log(sanmao);
sanmao.bark();
sanmao.sayName();
console.log(sanmao instanceof Dog); */

const isObject = function isObject(obj) {
  let type = typeof obj;
  return obj != null && (type === "object" || type === "function");
};
function _new(Ctor, ...params) {
  let obj, result;
  // 对Ctor做校验：必须是一个函数、必须具备prototype、不能是Symbol/BigInt函数
  if (typeof Ctor !== "function" || !Ctor.prototype || Ctor === Symbol || Ctor === BigInt) {
    throw new TypeError('Ctor is not a constructor');
  }
  obj = Object.create(Ctor.prototype);
  result = Ctor.call(obj, ...params);
  if (isObject(result)) return result;
  return obj;
}
let sanmao = _new(Dog, '三毛');
sanmao.bark(); //=>"wangwang"
sanmao.sayName(); //=>"my name is 三毛"
console.log(sanmao instanceof Dog); //=>true



/* // Ctor:创造哪个类的实例,就传递谁 -> Dog
// params:基于ES6的剩余运算符，获取除了第一个以外，其余传递的实参信息，结果是一个数组 -> ['三毛']
function _new(Ctor, ...params) {
  // @1 创建Ctor类的空实例对象「空对象、__proto__指向Ctor.prototype」
  let obj = {};
  obj.__proto__ = Ctor.prototype; //obj.__proto__ = Dog.prototype;

  // @2 把函数看作普通函数执行，让其this指向创建的实例对象（别忘记传递实参值）
  let result = Ctor.call(obj, ...params); //Dog.call(obj,‘三毛’) 执行Dog方法，把'三毛'传递进来，把Dog中的this改为实例对象，result获取的是Dog方法的返回值

  // @3 观察返回结果，决定返回啥
  if (isObject(result)) return result;
  return obj; //Dog执行返回值不是对象，则默认返回创建的实例对象
}
let sanmao = _new(Dog, '三毛');
sanmao.bark(); //=>"wangwang"
sanmao.sayName(); //=>"my name is 三毛"
console.log(sanmao instanceof Dog); //=>true */